const DEFAULT_FRAMERATE = 30;
const DEFAULT_DRAW_CURSOR = true;
+const PIPELINE_BLOCKLIST_FILENAME = 'gnome-shell-screencast-pipeline-blocklist';
+
const PIPELINES = [
{
+ id: 'swenc-dmabuf-vp8-vp8enc',
pipelineString:
'capsfilter caps=video/x-raw(memory:DMABuf),max-framerate=%F/1 ! \
glupload ! glcolorconvert ! gldownload ! \
webmmux',
},
{
+ id: 'swenc-memfd-vp8-vp8enc',
pipelineString:
'capsfilter caps=video/x-raw,max-framerate=%F/1 ! \
videoconvert chroma-mode=none dither=none matrix-mode=output-only n-threads=%T ! \
this._pipelineString = null;
this._framerate = DEFAULT_FRAMERATE;
this._drawCursor = DEFAULT_DRAW_CURSOR;
+ this._blocklistFromPreviousCrashes = [];
+
+ const pipelineBlocklistPath = GLib.build_filenamev(
+ [GLib.get_user_runtime_dir(), PIPELINE_BLOCKLIST_FILENAME]);
+ this._pipelineBlocklistFile = Gio.File.new_for_path(pipelineBlocklistPath);
+
+ try {
+ const [success_, contents] = this._pipelineBlocklistFile.load_contents(null);
+ const decoder = new TextDecoder();
+ this._blocklistFromPreviousCrashes = JSON.parse(decoder.decode(contents));
+ } catch (e) {
+ if (!e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.NOT_FOUND))
+ throw e;
+ }
this._pipelineState = PipelineState.INIT;
this._pipeline = null;
_bailOutOnError(message, errorDomain = ScreencastErrors, errorCode = ScreencastError.RECORDER_ERROR) {
const error = new GLib.Error(errorDomain, errorCode, message);
+ // If it's a PIPELINE_ERROR, we want to leave the failing pipeline on the
+ // blocklist for the next time. Other errors are pipeline-independent, so
+ // reset the blocklist to allow the pipeline to be tried again next time.
+ if (!error.matches(ScreencastErrors, ScreencastError.PIPELINE_ERROR))
+ this._updateServiceCrashBlocklist([...this._blocklistFromPreviousCrashes]);
+
this._teardownPipeline();
this._unwatchSender();
this._stopSession();
this._sessionProxy.connectSignal('Closed', this._onSessionClosed.bind(this));
}
+ _updateServiceCrashBlocklist(blocklist) {
+ try {
+ if (blocklist.length === 0) {
+ this._pipelineBlocklistFile.delete(null);
+ } else {
+ this._pipelineBlocklistFile.replace_contents(
+ JSON.stringify(blocklist), null, false,
+ Gio.FileCreateFlags.NONE, null);
+ }
+ } catch (e) {
+ console.log(`Failed to update pipeline-blocklist file: ${e.message}`);
+ }
+ }
+
_tryNextPipeline() {
const {done, value: pipelineConfig} = this._pipelineConfigs.next();
if (done) {
return;
}
+ if (this._blocklistFromPreviousCrashes.includes(pipelineConfig.id)) {
+ console.info(`Skipping pipeline '${pipelineConfig.id}' due to pipeline blocklist`);
+ this._tryNextPipeline();
+ return;
+ }
+
if (this._pipeline) {
if (this._pipeline.set_state(Gst.State.NULL) !== Gst.StateChangeReturn.SUCCESS)
log('Failed to set pipeline state to NULL');
try {
this._pipeline = this._createPipeline(this._nodeId, pipelineConfig,
this._framerate);
- } catch (error) {
- this._tryNextPipeline();
- return;
- }
- if (!this._pipeline) {
+ // Add the current pipeline to the blocklist, so it is skipped next
+ // time in case we crash; we'll remove it again on success or on
+ // non-pipeline-related failures.
+ this._updateServiceCrashBlocklist(
+ [...this._blocklistFromPreviousCrashes, pipelineConfig.id]);
+ } catch (error) {
this._tryNextPipeline();
return;
}
break;
case PipelineState.FLUSHING:
+ // The pipeline ran successfully and we didn't crash; we can remove it
+ // from the blocklist again now.
+ this._updateServiceCrashBlocklist([...this._blocklistFromPreviousCrashes]);
+
this._addRecentItem();
this._teardownPipeline();
if (error.matches(ScreencastErrors, ScreencastError.OUT_OF_DISK_SPACE)) {
// Translators: notification title.
this._showNotification(_('Screencast ended: Out of disk space'));
- return;
+ } else if (error.matches(ScreencastErrors, ScreencastError.SERVICE_CRASH)) {
+ // We can encourage user to try again on service crashes since the
+ // recorder will auto-blocklist the pipeline that crashed.
+
+ // Translators: notification title.
+ this._showNotification(_('Screencast ended unexpectedly, please try again'));
+ } else {
+ // Translators: notification title.
+ this._showNotification(_('Screencast ended unexpectedly'));
}
- // Translators: notification title.
- this._showNotification(_('Screencast ended unexpectedly'));
break;
}
}